package service

import (
	"context"
	"io"
	"mime/multipart"
	"net/http"
	"path/filepath"
	"time"

	"github.com/mars/internal/admin/core/domain"
	"github.com/mars/internal/pkg/wxcloud"
	"github.com/mars/pkg/errors"
	"github.com/mars/pkg/loggers"
	"github.com/mars/pkg/randid"
)

type ProductSrv interface {
	GetProductList(ctx context.Context, params domain.ProductQueryParams) ([]domain.Product, int64, error)
	GetProduct(ctx context.Context, productID string) (*domain.Product, error)
	GenerateUploadUrls(ctx context.Context, filenames []string) ([]*wxcloud.UploadFileResponse, error)
	UploadProductImages(ctx context.Context, files *multipart.Form) ([]string, error)
	CreateProduct(ctx context.Context, product *domain.Product) error
	UpdateProduct(ctx context.Context, productID string, product *domain.Product) error
	DeleteProduct(ctx context.Context, productID string) error
	GetProductsByType(ctx context.Context, productType domain.ProductType) ([]domain.Product, error)
	GetProductsByStatus(ctx context.Context, status domain.ProductStatus) ([]domain.Product, error)
	UpdateProductStatus(ctx context.Context, productID string, status domain.ProductStatus) error
	UpdateProductStock(ctx context.Context, productID string, stock int) error
	GetProductStats(ctx context.Context) (*domain.ProductStats, error)
	SearchProducts(ctx context.Context, keyword string) ([]domain.Product, error)
}

type productService struct {
	productDomain domain.ProductGateway
	randid        randid.RandIdGetter
}

var _ ProductSrv = (*productService)(nil)

func newProductService(fc domain.Factory) *productService {
	return &productService{
		productDomain: fc.ProductGateway(),
		randid:        randid.NewSecureRandom(16),
	}
}

// GetProductList implements ProductSrv.
func (p *productService) GetProductList(ctx context.Context, params domain.ProductQueryParams) ([]domain.Product, int64, error) {
	products, total, err := p.productDomain.GetProductList(ctx, params)
	if err != nil {
		loggers.Error(ctx, "get product list failed", err)
		return nil, 0, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "get product list failed")
	}
	return products, total, nil
}

// GetProduct implements ProductSrv.
func (p *productService) GetProduct(ctx context.Context, productID string) (*domain.Product, error) {
	product, err := p.productDomain.GetProduct(ctx, productID)
	if err != nil {
		loggers.Error(ctx, "get product failed", err)
		return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "get product failed")
	}
	return product, nil
}

// CreateProduct implements ProductSrv.
func (p *productService) CreateProduct(ctx context.Context, product *domain.Product) error {
	err := p.productDomain.CreateProduct(ctx, product)
	if err != nil {
		loggers.Error(ctx, "create product failed", err)
		return errors.NewWithStackWithoutCode(http.StatusInternalServerError, "创建商品失败")
	}
	return nil
}

// GenerateUploadUrls implements ProductSrv.
func (p *productService) GenerateUploadUrls(ctx context.Context, filenames []string) ([]*wxcloud.UploadFileResponse, error) {
	random, err := p.randid.Get()
	if err != nil {
		loggers.Error(ctx, "generate random id failed", err)
		return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "生成随机ID失败")
	}

	urls := make([]*wxcloud.UploadFileResponse, len(filenames))
	for i, filename := range filenames {
		filename = "products/" + time.Now().Format("20060102150405") + "-" + random + "-" + filepath.Ext(filename)
		uploadResp, err := p.productDomain.GenerateUploadUrl(ctx, filename)
		if err != nil {
			loggers.Error(ctx, "generate upload urls failed", err)
			return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "生成上传链接失败")
		}
		urls[i] = uploadResp
	}
	return urls, nil
}

// UpdateProduct implements ProductSrv.
func (p *productService) UpdateProduct(ctx context.Context, productID string, product *domain.Product) error {
	// 获取商品
	oldProduct, err := p.productDomain.GetProduct(ctx, productID)
	if err != nil {
		loggers.Error(ctx, "get product failed", err)
		return errors.NewWithStackWithoutCode(http.StatusInternalServerError, "获取商品失败")
	}

	// 校验商品
	if product.Price < 0 {
		return errors.New(http.StatusBadRequest, "价格不可以为负数")
	}
	if product.Stock != nil && *product.Stock < 0 {
		return errors.New(http.StatusBadRequest, "库存不可以为负数")
	}

	currentPrice := oldProduct.Price
	if product.Price > 0 {
		currentPrice = product.Price
	}

	if product.Specifications != nil {
		for _, spec := range product.Specifications {
			if spec.Stock < 0 {
				return errors.New(http.StatusBadRequest, "规格库存不可以为负数")
			}
			if currentPrice+spec.PriceAdjustment < 0 {
				return errors.New(http.StatusBadRequest, "规格价格调整不可以为负数")
			}
		}
	}

	// 失败的情况：商品不存在、服务器调用错误
	err = p.productDomain.UpdateProduct(ctx, productID, product)
	if err != nil {
		loggers.Error(ctx, "update product failed", err)
		return errors.NewWithStackWithoutCode(http.StatusInternalServerError, "商品更新失败")
	}
	return nil
}

// DeleteProduct implements ProductSrv.
func (p *productService) DeleteProduct(ctx context.Context, productID string) error {
	err := p.productDomain.DeleteProduct(ctx, productID)
	if err != nil {
		loggers.Error(ctx, "delete product failed", err)
		return errors.NewWithStackWithoutCode(http.StatusInternalServerError, "删除商品失败")
	}
	return nil
}

// GetProductsByType implements ProductSrv.
func (p *productService) GetProductsByType(ctx context.Context, productType domain.ProductType) ([]domain.Product, error) {
	products, err := p.productDomain.GetProductsByType(ctx, productType)
	if err != nil {
		loggers.Error(ctx, "get products by type failed", err)
		return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "获取商品失败")
	}
	return products, nil
}

// GetProductsByStatus implements ProductSrv.
func (p *productService) GetProductsByStatus(ctx context.Context, status domain.ProductStatus) ([]domain.Product, error) {
	products, err := p.productDomain.GetProductsByStatus(ctx, status)
	if err != nil {
		loggers.Error(ctx, "get products by status failed", err)
		return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "获取商品失败")
	}
	return products, nil
}

// UpdateProductStatus implements ProductSrv.
func (p *productService) UpdateProductStatus(ctx context.Context, productID string, status domain.ProductStatus) error {
	err := p.productDomain.UpdateProductStatus(ctx, productID, status)
	if err != nil {
		loggers.Error(ctx, "update product status failed", err)
		return errors.NewWithStackWithoutCode(http.StatusInternalServerError, "更新商品状态失败")
	}
	return nil
}

// UpdateProductStock implements ProductSrv.
func (p *productService) UpdateProductStock(ctx context.Context, productID string, stock int) error {
	// 检查商品是否存在
	product, err := p.productDomain.GetProduct(ctx, productID)
	if err != nil {
		loggers.Error(ctx, "product not found", err)
		return errors.NewWithStackWithoutCode(http.StatusNotFound, "未找到要更新的商品")
	}

	// 检查是否为实物商品
	if product.Type != domain.ProductTypePhysical {
		return errors.NewWithStackWithoutCode(http.StatusBadRequest, "只有实物商品可以更新库存")
	}

	if stock < 0 {
		return errors.NewWithStackWithoutCode(http.StatusBadRequest, "库存不能为负数")
	}

	err = p.productDomain.UpdateProductStock(ctx, productID, stock)
	if err != nil {
		loggers.Error(ctx, "update product stock failed", err)
		return errors.NewWithStackWithoutCode(http.StatusInternalServerError, "更新商品库存失败")
	}
	return nil
}

// GetProductStats implements ProductSrv.
func (p *productService) GetProductStats(ctx context.Context) (*domain.ProductStats, error) {
	stats, err := p.productDomain.GetProductStats(ctx)
	if err != nil {
		loggers.Error(ctx, "get product stats failed", err)
		return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "获取商品统计失败")
	}
	return stats, nil
}

// SearchProducts implements ProductSrv.
func (p *productService) SearchProducts(ctx context.Context, keyword string) ([]domain.Product, error) {
	if keyword == "" {
		return []domain.Product{}, nil
	}

	products, err := p.productDomain.SearchProducts(ctx, keyword)
	if err != nil {
		loggers.Error(ctx, "search products failed", err)
		return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "搜索商品失败")
	}
	return products, nil
}

// UploadProductImages implements ProductSrv.
func (p *productService) UploadProductImages(ctx context.Context, form *multipart.Form) ([]string, error) {
	// fileValidator := domain.NewFileValidator(
	// 	10*1024*1024, // 10MB
	// 	[]string{"image/jpeg", "image/jpg", "image/png", "image/gif"},
	// 	[]string{".jpg", ".jpeg", ".png", ".gif"},
	// 	10, // 最多10个文件
	// )
	fileIds := make([]string, 0, 3)
	files := form.File["files"]
	for _, fileHeader := range files {
		file, err := fileHeader.Open()
		if err != nil {
			loggers.Error(ctx, "open file failed", err)
			return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "打开文件失败")
		}
		defer file.Close()

		fileData, err := io.ReadAll(file)
		if err != nil {
			loggers.Error(ctx, "read file failed", err)
			return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "读取文件失败")
		}
		filename, err := p.generateFileName(fileHeader.Filename)
		if err != nil {
			loggers.Error(ctx, "generate file name failed", err)
			return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "生成文件名失败")
		}

		uploadResp, err := p.productDomain.GenerateUploadUrl(ctx, filename)
		if err != nil {
			loggers.Error(ctx, "generate upload url failed", err)
			return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "生成上传链接失败")
		}

		if uploadResp.ErrCode != 0 {
			loggers.Errorfc(ctx, "generate upload url failed: %d, %s", uploadResp.ErrCode, uploadResp.ErrMsg)
			return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "生成上传链接失败")
		}

		err = p.productDomain.UploadFile(ctx, uploadResp, filename, fileData)
		if err != nil {
			loggers.Error(ctx, "upload file failed", err)
			return nil, errors.NewWithStackWithoutCode(http.StatusInternalServerError, "上传文件失败")
		}
		fileIds = append(fileIds, uploadResp.FileID)
	}

	return fileIds, nil
}

func (p *productService) generateFileName(filename string) (string, error) {
	random, err := p.randid.Get()
	if err != nil {
		return "", errors.NewWithStackWithoutCode(http.StatusInternalServerError, "生成随机ID失败")
	}
	return "products/" + time.Now().Format("20060102150405") + "-" + random + "-" + filepath.Ext(filename), nil
}
